#include "../../public/_public.h"

using namespace idc;

ctcpclient tcpclient;
clogfile logfile;
cpactive pactive;

struct st_arg
{
    int clienttype; //客户端的类型  1-上传 2-下载
    char ip[32]; //服务端的ip地址
    int port;   //服务端的端口
    char clientpath[256];  //客户端的路径
    int ptype;   //操作后=本地文件的处理方式  1-删除 2-备份
    char serverpathbak[256];  //备份的路径
    bool andchild; //是否上传子目录的文件
    char matchname[256]; //文件名匹配的规则
    char serverpath[256]; //服务端的路径
    int timeval; //执行文件上传工作的时间间隔
    int timeout; //心跳超时时间
    char pname[256]; //进程名
}starg;

string strsendmessage,strrecvmessage;
void Exit(int sig);
bool login(const char* argv);
bool _xmlstrag(const char* xmlbuffer); //解析参数
void _help(); //帮助信息
bool _tcpgetfile(bool& bcontinue); //下载文件
bool recvfile(const string& filename,const string& mtime,const int filesize); //接收文件


int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        _help();
        return -1;
    }
    if(logfile.open(argv[1])==false)
    {
        printf("logfile open failed.\n");
        return -1;
    }
    if(_xmlstrag(argv[2])==false)
    {
        logfile.write("xmlstrag(%s) failed.\n",argv[1]);
        return -1;
    }
    pactive.init(starg.timeout,starg.pname);
    // logfile.write("%d\n%s\n%d\n%s\n%d\n%s\n%d\n%s\n%s\n%d\n%d\n%s\n",starg.clienttype,starg.ip,starg.port,starg.clientpath,starg.ptype,starg.clientpathbak,starg.andchild,starg.matchname,starg.serverpath,starg.timeval,starg.timeout,starg.pname);//仅做测试使用
    
    closeallsign();
    signal(SIGINT,Exit);
    signal(SIGTERM,Exit);
    if(tcpclient.Connect(starg.port,starg.ip)==false)
    {
        logfile.write("tcpclient.Connect(%s,%s) failed.\n",starg.ip,starg.port);
    }
    if(login(argv[2])==false)
    {
        logfile.write("login(%s) failed.\n",argv[2]);
        Exit(0);
    }
    Exit(0);
}


void Exit(int sig)
{
    logfile.write("程序退出，信号:%d\n",sig);
    exit(0);
}

bool login(const char* argv)
{
    sformat(strsendmessage,"%s<clienttype>1</clienttype>",argv);
    if(tcpclient.Write(strsendmessage)==false)
    {
        logfile.write("tcpclient.Write(%s) failed.\n",strsendmessage.c_str());
        return false;
    }
    if(tcpclient.Read(strrecvmessage,20)==false)
    {
        logfile.write("tcpclient.Read() failed.\n");
        return false;
    }
    if(strrecvmessage.find("<login>ok<login>")<0)
    {
        logfile.write("valid error.\n");
        return false;
    }
    logfile.write("login:%s\n",strrecvmessage.c_str());
    return true;
}

bool _tcpgetfile()
{
    while(true)
    {
        if(tcpclient.Read(strrecvmessage,1024)==false)
        {
            logfile.write("tcpclient.Read() failed.\n");
        }
        if(strrecvmessage=="<activetest>ok</activetest>")   //处理心跳报文
        {
            strsendmessage="ok";
            if(tcpclient.Write(strsendmessage)==false)
            {
                logfile.write("tcpclient.Write(%s) failed.\n",strsendmessage.c_str());
                return false;
            }
        }
        else if(strrecvmessage.find("<file>")!=string::npos)   //处理文件下载
        {
            string filename,mtime;
            int size;
            getxmlbuffer(strrecvmessage,"filename",filename,256);
            getxmlbuffer(strrecvmessage,"mtime",mtime,32);
            getxmlbuffer(strrecvmessage,"size",size);
            string clientfilename=filename;
            string clientpath=starg.clientpath;
            string serverpath=starg.serverpath;
            replacestr(clientfilename,serverpath,clientpath);
            if (recvfile(filename,mtime,size)==true)
            {
                logfile << "ok.\n";
                sformat(strsendmessage,"<filename>%s</filename><result>ok</result>",filename.c_str());
            }
            else
            {
                logfile << "failed.\n";
                sformat(strsendmessage,"<filename>%s</filename><result>failed</result>",filename.c_str());
            }
            if(tcpclient.Write(strsendmessage)==false)
            {
                logfile.write("recvfiles write failed.\n");
                return;
            }
        }
    }
}

bool recvfile(const string& filename,const int filesize,const string& mtime)
{
    int totalread = 0;
    char buffer[1024];
    int oneread=1024;
    cofile ofile;
    if(ofile.open(filename.c_str(),true,ios::out|ios::binary)==false)
    { 
        logfile.write("open file %s failed.\n",filename.c_str());
        return false;
    }
    while(true)
    {
        memset(buffer,0,sizeof(buffer));
        if(filesize-totalread<oneread)
        {
            oneread= filesize-totalread;
        }
        if(tcpclient.Read(buffer,oneread)==false)
        {
            logfile.write("recvfile read failed.\n");
            return false;
        }
        ofile.write(buffer,oneread);
        totalread+=oneread;
        if(totalread==filesize) break;
    }
    ofile.closeandrename();
    idc::setmtime(filename,mtime);
    return true;
}


bool _xmlstrag(const char* xmlbuffer)
{
    memset(&starg,0,sizeof(starg));
    starg.clienttype=1;
    getxmlbuffer(xmlbuffer,"ip",starg.ip,32);
    if(starg.ip==nullptr)
    {
        logfile.write("ip is null.\n");
        return false;
    }
    getxmlbuffer(xmlbuffer,"port",starg.port);
    if(starg.port==0)
    {
        logfile.write("port is invalid.\n");
        return false;
    }
    getxmlbuffer(xmlbuffer,"clientpath",starg.clientpath,256);
    if(starg.clientpath==nullptr)
    {
        logfile.write("clientpath is null.\n");
        return false;
    }
    getxmlbuffer(xmlbuffer,"ptype",starg.ptype);
    if(starg.ptype!=1&&starg.ptype!=2)
    {
        logfile.write("ptype is invalid.\n");
        return false;
    }
    if(starg.ptype==2)
    {
        getxmlbuffer(xmlbuffer,"serverpathbak",starg.serverpathbak,256);
        if(starg.serverpathbak==nullptr)
        {
            logfile.write("clientpathbak is null.\n");
            return false;
        }
    }
    getxmlbuffer(xmlbuffer,"andchild",starg.andchild);
    getxmlbuffer(xmlbuffer,"matchname",starg.matchname,256);
    if(starg.matchname==nullptr)
    {
        logfile.write("matchname is null.\n");
        return false;
    }
    getxmlbuffer(xmlbuffer,"serverpath",starg.serverpath,256);
    if(starg.serverpath==nullptr)
    {
        logfile.write("serverpath is null.\n");
        return false;
    }
    getxmlbuffer(xmlbuffer,"timeval",starg.timeval);
    if(starg.timeval==0)
    {
        logfile.write("timeval is invalid.\n");
        return false;
    }
    if(starg.timeval>30)
    {
        starg.timeval=30;
    }
    getxmlbuffer(xmlbuffer,"timeout",starg.timeout);
    if(starg.timeout==0)
    {
        logfile.write("timeout is invalid.\n");
        return false;
    }
    if(starg.timeout<=starg.timeval)
    {
        logfile.write("timeout<=timeval.\n");
        return false;
    }
    getxmlbuffer(xmlbuffer,"pname",starg.pname,256);
    if(starg.pname==nullptr)
    {
        logfile.write("pname is null.\n");
        return false;
    }
    return true;
}

void _help()
{
    printf("\n");
    printf("Using:/root/mylib/project/tools/bin/tcpputfiles logfilename xmlbuffer\n\n");

    printf("Sample:/root/mylib/project/tools/bin/procctl 20 /root/mylib/project/tools/bin/tcpputfiles /log/idc/tcpputfiles_surfdata.log "\
              "\"<ip>47.103.204.186</ip><port>8080</port>"\
              "<clientpath>/tmp/client</clientpath><ptype>1</ptype>"
              "<serverpath>/tmp/server</serverpath>"\
              "<andchild>true</andchild><matchname>*.xml,*.txt</matchname><timeval>10</timeval>"\
              "<timeout>50</timeout><pname>tcpputfiles_surfdata</pname>\"\n\n");

    printf("本程序是数据中心的公共功能模块，采用tcp协议把文件上传给服务端。\n");
    printf("logfilename   本程序运行的日志文件。\n");
    printf("xmlbuffer     本程序运行的参数，如下：\n");
    printf("ip            服务端的IP地址。\n");
    printf("port          服务端的端口。\n");
    printf("ptype         文件上传成功后的处理方式：1-删除文件；2-移动到备份目录。\n");
    printf("clientpath    本地文件存放的根目录。\n");
    printf("clientpathbak 文件成功上传后，本地文件备份的根目录，当ptype==2时有效。\n");
    printf("andchild      是否上传clientpath目录下各级子目录的文件，true-是；false-否，缺省为false。\n");
    printf("matchname     待上传文件名的匹配规则，如\"*.TXT,*.XML\"\n");
    printf("serverpath       服务端文件存放的根目录。\n");
    printf("timetvl       扫描本地目录文件的时间间隔，单位：秒，取值在1-30之间。\n");
    printf("timeout       本程序的超时时间，单位：秒，视文件大小和网络带宽而定，建议设置50以上。\n");
    printf("pname         进程名，尽可能采用易懂的、与其它进程不同的名称，方便故障排查。\n\n");
}
